unit timezone;

{$mode objfpc}{$H+}

interface

function GetLocalTimeOffset: Integer;
function UniversalTimeToLocal(UT: TDateTime): TDateTime;
function LocalTimeToUniversal(LT: TDateTime): TDateTime;


implementation
uses
  dateutils,
  {$IFDEF MSWINDOWS} windows {$ENDIF}
  {$IFDEF UNIX} unixutil {$ENDIF};

function GetLocalTimeOffset: Integer;
{$IFDEF MSWINDOWS}
var
  BiasType: Byte;
  TZInfo: TTimeZoneInformation;
const
  BS_TYPE_UKNOWN = 0 { Unknown };
  BS_TYPE_STANDARD = 1 { Standard Time };
  BS_TYPE_DAYLIGHT = 2 { Daylight Time };
{$ENDIF}
begin
  Result := 0;
  {$IFDEF MSWINDOWS}
  BiasType := GetTimeZoneInformation(TZInfo{%H-});
  if (BiasType=BS_TYPE_UKNOWN) then Exit;

  // Determine offset in effect for DateTime UT.
  if (BiasType=BS_TYPE_DAYLIGHT) then
    Result := TZInfo.Bias + TZInfo.DaylightBias
  else if (BiasType=BS_TYPE_STANDARD) then
    Result := TZInfo.Bias + TZInfo.StandardBias;
  {$ENDIF}
  {$IFDEF UNIX}
    Result := -Tzseconds div 60;
  {$ENDIF}
end;

function UniversalTimeToLocal(UT: TDateTime): TDateTime;
var
  LT: TDateTime;
  TZOffset: Integer;
begin
  LT := UT;
  TZOffset := GetLocalTimeOffset;

  // Apply offset.
  if (TZOffset > 0) then
    // Time zones west of Greenwich.
    LT := UT - EncodeTimeInterval(TZOffset div 60, TZOffset mod 60, 0, 0)
  else if (TZOffset = 0) then
    // Time Zone = Greenwich.
    LT := UT
  else if (TZOffset < 0) then
    // Time zones east of Greenwich.
    LT := UT + EncodeTimeInterval(Abs(TZOffset) div 60, Abs(TZOffset) mod 60, 0, 0);

  // Return Local Time.
  Result := LT;
end;

function LocalTimeToUniversal(LT: TDateTime): TDateTime;
var
  UT: TDateTime;
  TZOffset: Integer; // Offset in minutes.
begin
  // Initialize UT to something,
  // so compiler doesn't complain.
  UT := LT;
  // Determine offset in effect for DateTime LT.
  TZOffset := GetLocalTimeOffset;
  // Apply offset.
  if (TZOffset > 0) then
    // Time zones west of Greenwich.
    UT := LT + EncodeTimeInterval(TZOffset div 60, TZOffset mod 60, 0, 0)
  else if (TZOffset = 0) then
    // Time Zone = Greenwich.
    UT := LT
  else if (TZOffset < 0) then
    // Time zones east of Greenwich.
    UT := LT - EncodeTimeInterval(Abs(TZOffset) div 60, Abs(TZOffset) mod 60, 0, 0);

  // Return Universal Time.
  Result := UT;
end;

end.

